package com.csw.rediscachedemo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * spring中使用redis可以用这种java配置类的方式配置相关bean（相关配置百度）
 * 对于springboot来说，自动配置已经帮我们注入jedisConnectionFactory，以及redisTemplate，StringRedisTemplate
 * 所以不需要在做了，只需要在配置文件做相关连接池以及redis的连接配置即可 不过自动配置的RedisTemplate<Object,
 * Object>默认使用jdk的序列化机制，我们需要配置json的序列化 Jackson2JsonRedisSerializer
 * 以及cache的缓存管理器配置
 *
 * @author 刘彦军
 */
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    /**
     * 定义自己的redisTemplate对象，如果容器中有redisTemplate对象，
     * springboot将不在注入它默认的redisTemplate对象 也可以不用指定name属性，那么方法名一定要为redisTemplate
     * （应为bean默认的名称为方法名,这个机制也可以排除springboot注入默认redisTemplate）
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
                Object.class);
        //（取出数据序列化配置）
        // 配置在使用template对象从缓存读取json字符串的时候，无法将json字符串转换为java Bean的问题，不配置则抛出异常
        // （java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to
        // com.springboot.enty.Stu）
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //（存入数据序列化配置）
        // key序列化方式
        template.setKeySerializer(redisSerializer);
        // value存入序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    /**
     * 自定义缓存管理器，注入RedisCacheManager（不使用springboot默认提供的），
     * 下面缓存管理器配置了缓存失效时间，(如果有其他需求，需要重新定义缓存管理器，在使用缓存注解时指定对应的缓存管理器)
     * 过期时间只对Cache的那几个注解有效比如（@Cacheable，@CachePut），跟redisTemplate对象添加的缓存无关
     * 以及cache注解存取数据的序列化设置
     *
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
                Object.class);
        //（取出数据序列化配置）
        // 配置在使用 @Cacheable(value="'stus'",key="'stu:1'")注解，当第二次从缓存中读取
        // 数据时，无法将json字符串转换为java Bean的问题，不配置则抛出异常
        // （java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to
        // com.springboot.enty.Stu）
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //（存入数据序列化配置）
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheConfiguration redisCacheConfiguration = config
                //配置缓存失效时间100秒
                .entryTtl(Duration.ofSeconds(100))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));

        //原生配置管理器，时间是统一的 .entryTtl(Duration.ofSeconds(100))
        //RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();

        //自定义配置管理器 @Cacheable(value = "aa:bb:c:e#30", key = "#id", unless = "#result == null ")
        //#30就是30s  ，此时上面的统一时间设置.entryTtl(Duration.ofSeconds(100))就是无效的
        CustomizedRedisCacheManager cacheManager = new CustomizedRedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(factory), redisCacheConfiguration);
        return cacheManager;
    }

}
//————————————————
//
//版权声明：本文为博主原创文章，遵循 CC 4.0 BY-SA 版权协议，转载请附上原文出处链接和本声明。
//
//原文链接：https://blog.csdn.net/qq_42962779/article/details/102651598
